/*******************************************************
版本描述:该版本新增两个线程，一个负责接收数据，一个负责发送数据，目前仅搭建了两个线程框架。
主框架描述:
    int main(int argc, char *argv[])
    {
        // 1、创建原始套接字
        // 2、接收数据（创建线程1）
        // 3、发送数据（创建线程2）
        return 0;
    }
编译方法:gcc -g -pthread sockraw-0.2.c
运行方法:sudo ./a.out  (运行后再ping该主机所在IP)
*******************************************************/
#include <pcap.h>

#include <net/if.h>// struct ifreq
#include <sys/ioctl.h> // ioctl、SIOCGIFADDR
#include <sys/socket.h> // socket
#include <netinet/ether.h> // ETH_P_ALL
#include <netpacket/packet.h> // struct sockaddr_ll

#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>

#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <pthread.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <errno.h>

/* red and flash print */
#define PRINT_POSITION_ERRNO(str,errorno) do { \
	fprintf(stderr,"\033[;31;5m%s:%s:%d:%s:%s\033\[0m\n", \
			__FILE__,__func__,__LINE__,\
			str,strerror(errorno)); } while(0);

void *thread_recv_handle(void *arg);
void *thread_send_handle(void *arg);

int main(int argc, char *argv[])
{
    unsigned int recv_len = 0;
    unsigned int send_len = 0;
    int sock_raw_fd = 0;
    unsigned char buf_recv[1024] = {0};
    unsigned char buf_send[1024] = {0};

    /* 01. create socket */
    //int socket ( int family, int type, int protocol );
    //family：协议族 这里写 PF_PACKET
    //type：  套接字类，这里写 SOCK_RAW
    //protocol：协议类别，指定可以接收或发送的数据包类型，不能写 “0”，取值如下，注意，传参时需要用 htons() 进行字节序转换。
    //ETH_P_IP：IPV4数据包
    //ETH_P_ARP：ARP数据包
    //ETH_P_ALL：任何协议类型的数据包
	sock_raw_fd = socket(PF_PACKET,SOCK_RAW,htons(ETH_P_ALL));
	if (sock_raw_fd < 0) {
		PRINT_POSITION_ERRNO("socket",errno);
		return errno;
	}

    /* 02. set misc mode */
    //通过命令给Linux网卡设置混杂模式：ifconfig eth0 promisc
    //通过命令给Linux网卡取消混杂模式：ifconfig eth0 -promisc
    //通过代码给Linux网卡设置混杂模式:
    struct ifreq ethreq = {0};
    strncpy(ethreq.ifr_name,"ens33",IFNAMSIZ);
    if (ioctl(sock_raw_fd,SIOCGIFFLAGS,&ethreq)) 
    {
        PRINT_POSITION_ERRNO("ioctl",errno);
        close(sock_raw_fd);
        exit(-1);
    }
    ethreq.ifr_flags |= IFF_PROMISC;
    if (ioctl(sock_raw_fd,SIOCSIFFLAGS,&ethreq))
    {
        PRINT_POSITION_ERRNO("ioctl",errno);
        close(sock_raw_fd);
        exit(-1);
    }

    /* 03. receive net packets from network */
    //sockfd:原始套接字
    //buf：接收数据缓冲区
    //nbytes:接收数据缓冲区的大小
    //flags：套接字标志(常为0)
    //from：这里没有用，写 NULL
    //addrlen：这里没有用，写 NULL
    pthread_t thread_recv = 0;
    void *arg_recv = NULL;
    if(pthread_create(&thread_recv,NULL,thread_recv_handle, arg_recv))
    {
        PRINT_POSITION_ERRNO("pthread_create",errno);
        close(sock_raw_fd);
        exit(-1);
    }
    recv_len = recvfrom(sock_raw_fd, buf_recv, sizeof(buf_recv), 0, NULL, NULL);
	printf("Recv Network Packet Successfully !!!\n");

    /* 04. 将网络接口赋值给原始套接字地址结构 */
    struct sockaddr_ll sll;//原始套接字地址结构
    struct ifreq req;//网络接口地址
    strncpy(req.ifr_name, "ens33", IFNAMSIZ);//指定网卡名称
    if(-1 == ioctl(sock_raw_fd, SIOCGIFINDEX, &req))//获取网络接口
    {
        PRINT_POSITION_ERRNO("ioctl",errno);
        close(sock_raw_fd);
        exit(-1);
    }
    /*将网络接口赋值给原始套接字地址结构*/
    bzero(&sll, sizeof(sll));
    sll.sll_ifindex = req.ifr_ifindex;


    /* 05. send packets to network */
    //sockfd：原始套接字
    //buf：发送数据缓冲区
    //nbytes:发送数据缓冲区的大小
    //flags：一般为 0
    //to：本机网络接口，指发送的数据应该从本机的哪个网卡出去，而不是以前的目的地址
    //addrlen：to 所指向内容的长度
    pthread_t thread_send = 0;
    void *arg_send = NULL;
    if(pthread_create(&thread_send,NULL,thread_send_handle, arg_send))
    {
        PRINT_POSITION_ERRNO("pthread_create",errno);
        close(sock_raw_fd);
        exit(-1);
    }
    send_len = sendto(sock_raw_fd, buf_send, sizeof(buf_send), 0 , (struct sockaddr *)&sll, sizeof(sll));
    if(send_len == -1) {
        PRINT_POSITION_ERRNO("sendto",errno);
        close(sock_raw_fd);
        exit(-1);
    }
	printf("Send Network Packet Successfully !!!\n");
	return 0;
}

void *thread_recv_handle(void *arg)
{
    pthread_exit(NULL);
    return NULL;
}

void *thread_send_handle(void *arg)
{
    pthread_exit(NULL);
    return NULL;
}

